package com.atguigu.hbase;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

/**
 * Created by Smexy on 2022/8/17
 *
 * 套路：
 *          所有的客户端去连接数据库，要看是否遵循JDBC。
 *              如果遵守JDBC(支持SQL)，按照JDBC的套路去连接数据库。
 *              Nosql(hbase,es,redis)都不遵守JDBC，按照以下套路操作
 *                      ①新建一个客户端
 *                      ②客户端和服务端建立连接
 *                      ③客户端发送命令到服务端执行
 *                      ④如果是读命令，接收服务端返回的结果
 *
 *
 *          构造一个对象时:
 *              ① new A()
 *              ②把类的构造器私有，不允许new
 *                      建造者模式: A a = new A.Builder().build()
 *                      工厂模式:   A a = new AFactory().getAInstance()
 *  ----------------------------------------
 *
 *          Connection:  通过ConnectionFactory构造
 *          谁创建Connection，谁就负责调用close关闭，释放资源！
 *
 *          Connection拥有连接master,regionserver,缓存meta表，连接zk的功能。
 *                      从Connection中获取Table和Admin。
 *
 *          Table： 负责对数据的CRUD(DML)
 *          Admin： 负责对表的操作(DDL),建表，删表，建库等
 *
 *          特点：
 *                   Connection的创建是重量级(费劲，消耗资源多)的，线程安全。建议一个App只创建一次，在不同的线程中可以共享。
 *
 *                   Table和Admin的创建都是轻量级(省事，简单)，线程不安全，建议每一个App的线程有自己的Table，Admin对象。
 *
 *
 *    ------------------------------
 *      Connection 如何知道hbase集群在哪里？
 *              不管是读还是写，第一步是访问zk，找到meta表的rs,读取meta表，查阅meta，知道region的regionserver
 *
 *              只需要告诉客户端zk中hbase的节点，客户端自动读取meta表向指定的rs发请求。
 *
 *     ---------------------------------
 *      数据进行CRUD:
 *              增,改:  Table.put(Put a)
 *                      一个Put代表对一个Cell的增或改操作
 *               删:   Table.delete(Delete b)
 *                      一个Delete对象对 1行|1列|1个cell|1个列族 的删除操作
 *               查单行:    Table.get(Get a)
 *                        一个Get代表查询单行
 *
 *               查多行:    Table.scan(Scanner scan)
 *                          一个Scanner代表scan的细节(从哪里开始，到哪结束等)
 *
 *
 *     ----------------------------------
 *      工具类:
 *              Bytes
 *                      把任意类型转为byte[] ：  Bytes.toBytes(T t)
 *                      把byte[] 转为任意类型:  Bytes.toXxx(byte[] b)
 *
 *
 *              CellUtil:
 *                      CellUtil.cloneXxx(): 获取一个Cell中的指定 Xxx(rowkey,列族，列名，ts，值)属性
 *
 *
 *
 */
public class HBaseUtil
{
    //希望加载HBaseUtil，就自动拥有连接
    private static Connection conn;

    static {
        try {
            /*
                HBaseConfiguration.create(): 返回一个Configuratioin
                                         Configuration conf = new Configuration();
                                         conf.addResource("hbase-default.xml");
                                         conf.addResource("hbase-site.xml");

                new Configuration()
             */
            //自动读取hbase的默认和自定义的配置文件
            conn = ConnectionFactory.createConnection();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
         构造Put

         put '表名','rowkey','列族名:列名','值'
     */
    public static Put getPut(String rk,String cf,String cq,String value){
        //声明put对哪一行进行put
        Put put = new Put(Bytes.toBytes(rk));
        //  a.xxx() 返回 a : 称为java语法糖
        return put.addColumn(Bytes.toBytes(cf),Bytes.toBytes(cq),Bytes.toBytes(value));
    }

    //用来遍历一个Result(一行)
    public static void parseResult(Result result){

        Cell[] cells = result.rawCells();

        for (Cell cell : cells) {

            System.out.println("rowkey:"+Bytes.toString(CellUtil.cloneRow(cell)));
            System.out.println(Bytes.toString(CellUtil.cloneFamily(cell)) +":"+
                Bytes.toString(CellUtil.cloneQualifier(cell)) +
                "==>" + Bytes.toString(CellUtil.cloneValue(cell)));

        }

    }

    /*
        返回指定表的Table对象实例
     */
    public static Table getTable(String tableName) throws IOException {

        /*
            isBlank(str): 判断str是否是null,"",或"        "
         */
        if (StringUtils.isBlank(tableName)){
            throw new RuntimeException("表名非法!");
        }

        Table table = conn.getTable(TableName.valueOf(tableName));

        return table;

    }


    public static void main(String[] args) {

        System.out.println(conn);

    }
    //关闭连接
    public static void closeConn() throws IOException {

            if (conn != null){
                conn.close();
            }

    }


}
